#ifndef __NET_MANAGER_H__
#define __NET_MANAGER_H__

#include <cstdint>
#include <thread>
#include <string>
#include <queue>
#include <mutex>
#include <vector>
#include <map>
#include <set>
#include <atomic>

#include "INetEventListener.h"
#include "ThreadSafeQueue.hpp"
#include "NetPeer.h"

namespace LiteNetLib
{
    class NetManager;
    class NetPacket;
    class ConnectionRequest;
    class NetConnectRequestPacket;
    class NetEvent;

    typedef net_endpoint net_endpoint;

    enum class NetEventType
    {
        Connect,
        Disconnect,
        Receive,
        ReceiveUnconnected,
        Error,
        ConnectionLatencyUpdated,
        Broadcast,
        ConnectionRequest,
        MessageDelivered
    };

    class NetManager
    {
        friend class NetEvent;
    private:
        asio::io_context m_io_context;
        asio::ip::udp::socket m_udp_socket;
        net_endpoint m_remote_endpoint;
        char m_recv_buffer[2048];

        std::thread *m_recvDataThread = nullptr;
        std::thread *m_logicThread = nullptr;

        // 
        INetEventListener *m_netEventListener;

        // 
        std::map<size_t, NetPeerPtr> m_peersMap;
        std::mutex m_peersMapMutex;

        //
        std::map<size_t, ConnectionRequest *> m_requestMap;
        std::mutex m_requestMapMutex;

        NetEvent *m_netEventPoolHead = nullptr;
        std::mutex m_netEventPoolMutex;
        ThreadSafeQueue<NetEvent*> m_netEventsQueue;

        bool m_useInternalThread = false;

        uint32_t m_clearPackageCountTime = 0;
        uint32_t m_clearBanTime = 0;

        std::set<std::string> m_banIpSet;
        std::mutex m_banIpMutex;

        std::map<std::string, uint32_t> m_limitPackageMap;
        std::mutex m_limitPackageMapMutex;

    public:
        bool unconnectedMessagesEnabled = true;
        bool broadcastReceiveEnabled = false;

        // 是否异步处理事件
        bool unsyncedEvents = false;
        // 收到以连接上的玩家的数据是否异步处理
        bool unsyncedReceiveEvent = false;
        // 连接或断开异步处理
        bool unsyncedConnectAndDisconnectEvent = false;

        int32_t updateTime = 15;
        int32_t pingInterval = 1000;
        int32_t disconnectTimeout = NetConstants::DisconnectTimeout;

        int32_t mtuOverride = 0;
        bool useSafeMtu = false;

    public:
        NetManager(INetEventListener *listener);
        ~NetManager();


        inline uint32_t getPeerCount();
        inline void disconnectPeer(NetPeerPtr peer);
        inline void disconnectPeer(NetPeerPtr peer, const char *disconnectData, uint32_t dataLength);

        void disconnectPeerForce(NetPeerPtr peer, DisconnectReason reason, NetPacket *packet);

        NetPeerPtr onConnectionSolved(ConnectionRequest *request, const char *rejectData, uint32_t rejectDataLength);

        void pollUpdate(uint32_t dt); // dt 的单位: ms
        void pollEvents();

        void createReceiveEvent(NetPacket *packet, DeliveryMethod method, int32_t headerSize, NetPeerPtr fromPeer);

        /***********************************************************************************************************
         * @brief 向特定的 ip 发送数据包，并回收数据包
         * @param packet 数据包
         * @param endpoint ip 地址
        ************************************************************************************************************/
        void sendNetPacketAndRecycle(NetPacket *packet, net_endpoint* endpoint);

        /***********************************************************************************************************
         * @brief 向特定的 ip 发送数据包，但是不会回收数据包，数据包要自行处理
         * @param packet 数据包
         * @param endpoint ip 地址
        ************************************************************************************************************/
        void sendNetPacket(NetPacket *packet, net_endpoint *endpoint);

        /***********************************************************************************************************
         * @brief 向特定的 ip 发送数据
         * @param buff 数据
         * @param buffSize 数据大小
         * @param endpoint ip 地址
        ************************************************************************************************************/
        void sendRaw(const char* buff, uint32_t buffSize, net_endpoint *endpoint);

        /***********************************************************************************************************
         * @brief 创建udp服务
         * @param host 绑定ip地址，0.0.0.0 为ipv4地址, :: 为ipv6地址
         * @param port 网络端口
         * @param useInternalThread 使用内部线程
         * @return true-开启成功，false-开启失败
        ***********************************************************************************************************/
        bool start(const char* host, uint16_t port, bool useInternalThread = true);

        /***********************************************************************************************************
         * @brief 判断 udp 服务是否还在运行中
        ***********************************************************************************************************/
        bool isRunning();

        /***********************************************************************************************************
         * @brief 关闭 udp 服务
         * @note 关闭服务后，不会触发客户端关闭事件
        ***********************************************************************************************************/
        void stop();

    private:

        void addPeer(NetPeerPtr peer);

        void removePeer(NetPeerPtr peer);

        void disconnectPeer(NetPeerPtr peer, DisconnectReason reason, bool force, const char *disconnectData, uint32_t dataLength, NetPacket *eventData);


        void updateLogic();
        


        void createEvent(NetEventType type, NetPeerPtr peer, net_endpoint *endpoint, DisconnectReason disconnectReason, ConnectionRequest *connectionRequest, DeliveryMethod deliveryMethod, NetPacket *readerSource);

        void processEvent(NetEvent *netEvent);

        void recycleEvent(NetEvent *netEvent);

        // void onReceiveMessage(asio::error_code ec, std::size_t bytes_recvd);

        void dataReceived(char *dataBuff, uint32_t dataSize, net_endpoint *endpoint);

        void processConnectRequest(net_endpoint *endpoint, NetPeerPtr peer, NetConnectRequestPacket *connRequest);

    };

    inline uint32_t NetManager::getPeerCount()
    {
        std::lock_guard<std::mutex> lk(m_peersMapMutex);
        return m_peersMap.size();
    }
    inline void NetManager::disconnectPeer(NetPeerPtr peer)
    {
        disconnectPeer(peer, nullptr, 0);
    }
    inline void NetManager::disconnectPeer(NetPeerPtr peer, const char *disconnectData, uint32_t dataLength)
    {
        disconnectPeer(peer, DisconnectReason::DisconnectPeerCalled, false, disconnectData, dataLength, nullptr);
    }


    class NetEvent
    {
    public:
        NetEvent *nextNetEvent = nullptr;

    public:
        // 事件类型
        NetEventType type;

        NetPeerPtr peer;
        // 客户端地址
        net_endpoint endpoint;
        // 数据包
        NetPacket *packet = nullptr;

        int32_t headerSize = 0;

        DeliveryMethod deliveryMethod;

        DisconnectReason disconnectReason;

        NetManager *netManager;

        ConnectionRequest *connectionRequest = nullptr;

        // 是否手动回收，若使用手动回收的话，需要调用 recycle 来回收该事件，不然会内存泄漏
        bool manualRecycle = false;

        inline NetEvent(NetManager *manager) : netManager(manager) {}

        inline void needManualRecycle()
        {
            manualRecycle = true;
        }

        inline void recycle()
        {
            netManager->recycleEvent(this);
        }
    };

}

#endif
